<script setup>
import { ref, watchEffect, computed, onMounted, nextTick } from 'vue';
import { useRouter } from 'vue-router'
import { useKeywordStore } from '@/stores/keyword'
import { message } from 'ant-design-vue';
import avatar from '@/assets/img/avatar.png';
import robot from '@/assets/img/robot.png';
import send from '@/assets/img/send.png';
import copy from '@/assets/img/copy.png';
import reload from '@/assets/img/reload.png';
import backIcon from '@/assets/img/mengbanzu.png';
import { post } from '@/utils/request';

// import MarkdownPreviewer from '@/components/markdown-previewer.vue';
import { marked } from 'marked';

const router = useRouter()

const store = useKeywordStore()

const rawKeyword = computed(() => store.getKeyword ?? '')

// 定义查询loading防止，重复操作
const loading = ref(false);

const uqinueId = ref(0);

// 查询输入内容
const comment = ref('');

// 提问回答数据
const qa = ref([
  // {
  //   id: 1,
  //   question: '快速提取科技政策中的亮点措施',
  //   answer: 'Hi，我是你的科技政策分析专家“科技政策汇”！请输入科技政策全文内容，我将为你提取亮点措施内容。'
  //   commandList: ['快速提取科技政策中的关键词', '看看科技政策的分类', '看看科技政策亮点措施的分类']
  // },
]);

onMounted(() => {
  // console.log('onMounted')
  let initList = []
  try {
    const cacheChatList = [] // 不用缓存
    // const cacheChatList = JSON.parse(localStorage.getItem('chatList')) || []
    initList.push(...cacheChatList)
  } catch (e) {
    console.error(e)
  }

  initList = initList.slice(-50)

  const submitMessages = initList.reduce((acc, cur) => {
    const nextItem = []
    nextItem.push({
      role: 'user',
      content: cur.question,
    })
    nextItem.push({
      role: 'assistant',
      content: cur.answer,
    })
    if (cur.commandList?.length > 0) {
      nextItem.push({
        role: 'user',
        content: `根据 "${cur.question}" 指令，生成三个相似用户想要问的指令，用空格隔开这三个指令`,
      })
      nextItem.push({
        role: 'assistant',
        content: cur.commandList.join(' '),
      })
    }
    return acc.concat(nextItem)
  }, [])

  if (rawKeyword.value) {
    const keyword = rawKeyword.value
    submitMessages.push({
      role: 'user',
      content: keyword
    })
    store.setKeyword('')
    qa.value = [...initList]

    const params = {
      model: 'string',
      messages: submitMessages,
      tools: [],
      do_sample: true,
      temperature: 0,
      top_p: 0,
      n: 1,
      max_tokens: 0,
      stop: 'string',
      stream: true
    }

    // 首页跳转过来的输入, 直接查询
    searchComment(keyword, params, false)
  } else {
    // 直接赋值
    qa.value = [...initList]
  }
  nextTick(() => {
    scrollToTop()
  })
})

// 控制回答文字输出效果
const displayedAnswer = ref('');
const currentAnswer = ref('');

const scrollToTop = () => {
  const messages = document.querySelector('.messages-wrap');
  if (messages) {
    messages.scrollTop = messages.scrollHeight + 200;
  }
};

// 格式化用户提问参数
const formatSingelUserQuestion = (message = '') => {
  const oldMessages = []
  for (const item of qa.value) {
    oldMessages.push({
      role: 'user',
      content: item.question,
    })
    oldMessages.push({
      role: 'assistant',
      content: item.answer,
    })
  }

  return {
    model: 'string',
    messages: [
      ...oldMessages,
      {
        role: 'user',
        content: message,
        // tool_calls: []
      }
    ],
    tools: [],
    do_sample: true,
    temperature: 0,
    top_p: 0,
    n: 1,
    max_tokens: 0,
    stop: 'string',
    stream: true
  }
}

/** 
 * 触发问题查询 
 *  @param {string} value 查询内容
 *  @param {object} params 查询提交参数
 *  @param {boolean} clearFlag 是否清空输入框
 */
const searchComment = async (value = '', params, clearFlag = true) => {
  if ((!value || /^[\r\n\s]*$/.test(value?.trim()))) {
    message.warn('请输入查询内容');
    return;
  }

  const sendValue = (value || comment.value);

  if (loading.value) {
    // message.warn('系统繁忙中');
    return; // 防止重复操作
  }
  /** 
   * @desc 获取查询结果
   *
   // 先清空，输入的搜素内容
   // 展示在对话框
   // 等待接口返回，展示回答答案
  */
  if (clearFlag) {
    comment.value = '';
  }
  uqinueId.value++;
  displayedAnswer.value = '';
  qa.value.push({
    id: uqinueId.value,
    question: sendValue,
    notReady: true,
    answer: '...'
  });
  loading.value = true
  const res = await fetch('/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(params),
  }).catch(e => {
    console.error(e)
    loading.value = false
  })
  if (res?.status >= 200 && res?.status < 300) {
    // 模拟成功结果
    const reader = res.body.getReader();
    const decoder = new TextDecoder();
    let text = '';
    let currentId = uqinueId.value;

    while (true) {
      const { done, value } = await reader.read();
      if (done) {
        loading.value = false
        break;
      }
      const singleRes = decoder.decode(value, { stream: true });
      const lines = singleRes.split('\n');
      for (const line of lines) {
        if (line.startsWith('data:')) {
          const jsonStr = line.substring(5);
          let jsonObj = {}
          try {
            jsonObj = JSON.parse(jsonStr)
            currentId = jsonObj?.id
            text += jsonObj?.choices?.[0]?.delta?.content || ''
            qa.value[qa.value.length - 1].id = currentId
            qa.value[qa.value.length - 1].answer = text
            nextTick(() => {
              scrollToTop()
            })
          } catch (err) {
            console.log("err", err)
          }
        }
      }
    }

    loading.value = false
    if (currentId) {
      qa.value[qa.value.length - 1].id = currentId
      generateCommand(sendValue, currentId)
    }
    nextTick(() => {
      scrollToTop()
    })
  } else {
    loading.value = false
    qa.value[qa.value.length - 1].answer = res?.statusText || '查询失败，请稍后再试'
    message.error(res?.statusText || '查询失败，请稍后再试');
  }
  // qa.value.push({
  //   id: qa.value.length + 1,
  //   question: sendValue,
  //   answer: 'Hi，我是你的科技政策分析专家“科技政策汇”！请输入科技政策全文内容，我将为你提取亮点措施内容。'
  // });

};

const generateCommand = async (text = '', chatId) => {
  const res = await post('/v1/chat/completions', {
    model: 'string',
    messages: [
      {
        role: 'user',
        content: `根据 "${text}" 指令，生成三个相似用户想要问的指令，用空格隔开这三个指令`,
      }
    ],
    tools: [],
    do_sample: true,
    temperature: 0,
    top_p: 0,
    n: 1,
    max_tokens: 0,
    stop: 'string',
    stream: false
  })
  if (res) {
    const values = qa.value
    for (const v of values) {
      if (v.id === chatId) {
        v.commandList = (res?.choices?.[0]?.message?.content.split(' ') || []).filter(str => !!str?.trim())
        break
      }
    }
    qa.value = values

    // 缓存
    window.localStorage.setItem('chatList', JSON.stringify(
      qa.value.map(v => ({ ...v, isCacheMsg: true }))
    ))
  }
}

// 快捷指令 
const commandOne = (text = '') => {
  // console.log('这是一条快捷指令', text);
  if (!text) return;
  searchComment(text, formatSingelUserQuestion(text), false)
};

// watchEffect(() => {
//   if (qa.value?.length > 0) {
//     let charIndex = 0;
//     const latestAnswer = qa.value[qa.value.length - 1].answer;
//     if (latestAnswer === '...') return;
//     currentAnswer.value = latestAnswer;
//     let inTag = false;
//     let tagName = '';
//     let tagStartString = '';
//     const interval = setInterval(() => {
//       if (charIndex < latestAnswer.length) {
//         let char = latestAnswer[charIndex];
//         if (!inTag && char === '<') {
//           // 可能是标签开始
//           let nextChars = latestAnswer.substring(charIndex + 1);
//           const tagStartMatch = nextChars.match(/(^[a-zA-Z][a-zA-Z0-9]*)(\s+class="[\w-]+"\s*)?>/);
//           /**
//            * @example 'asdas>'.match(/(^[a-zA-Z][a-zA-Z0-9]*)>/)
//            * @result ['asdas>', 'asdas', index: 0, input: 'asdas>', groups: undefined]
//            */
//           if (tagStartMatch) {
//             inTag = true;
//             tagName = tagStartMatch[1];
//             tagStartString += char + tagName + '>';
//             if (tagName === 'pre') {
//               displayedAnswer.value += char + tagName + '>';
//             }
//             charIndex += tagName.length + 2;

//             // 检查是否是自闭合标签
//             if (latestAnswer[charIndex] === '/') {
//               charIndex++;
//               const endTagMatch = latestAnswer.match(new RegExp(`^>/`));
//               if (endTagMatch) {
//                 displayedAnswer.value += tagStartString + endTagMatch[0];
//                 charIndex++;
//                 inTag = false;
//                 tagName = '';
//                 tagStartString = '';
//               }
//             }
//           } else {
//             // 不是标签开始，输出字符
//             displayedAnswer.value += char;
//             charIndex++;
//           }
//         } else if (inTag) {
//           if (char === '<') {
//             // 在标签内, 是否是标签结束
//             if (latestAnswer.slice(charIndex, charIndex + 3 + tagName.length) === `</${tagName}>`) {
//               inTag = false;
//               charIndex += 3 + tagName.length;
//               displayedAnswer.value += `</${tagName}>`;
//               tagName = '';
//             } else {
//               // 是否是其他标签开始或者结束
//               let nextChars = latestAnswer.substring(charIndex + 1);
//               const tagStartOrEnd = nextChars.match(/(^\/?[a-zA-Z][a-zA-Z0-9]*)(\s+class="[\w-]+"\s*)?>/);
//               if (tagStartOrEnd) {
//                 charIndex += tagStartOrEnd[0].length + 1;
//                 displayedAnswer.value += char + tagStartOrEnd[0];
//                 tagStartString = '';
//               } else {
//                 displayedAnswer.value += char;
//                 charIndex++;
//               }
//             }
//           } else {
//             displayedAnswer.value += tagStartString + char;
//             tagStartString = '';
//             charIndex++;
//           }
//         } else {
//           displayedAnswer.value += char;
//           charIndex++;
//         }
//         nextTick(() => {
//           scrollToTop()
//         });
//       } else {
//         loading.value = false;
//         qa.value[qa.value.length - 1].notReady = false
//         clearInterval(interval);
//         nextTick(() => {
//           scrollToTop()
//         });
//       }
//     }, 50);
//   }
// }, []);

const copyToClipboard = (text) => {
  navigator.clipboard.writeText(text).then(() => {
    message.success('内容已复制到剪贴板');
  });
};

</script>
<template>
  <div class="chat">
    <div class="messages-wrap">
      <div class="container" style="height: 100%;">
        <div v-for="(item, index) in qa" :key="item.id" class="message">
          <div class="content">
            <div class="question-content">
              <div class="question" v-html="marked(item.question)">
              </div>
              <div class="avatar">
                <img :src="avatar" alt="User Avatar" />
              </div>
            </div>

            <div class="answer-content">
              <div class="avatar">
                <img :src="robot" alt="robot Avatar" />
              </div>
              <a-popover v-if="item.commandList?.length > 0" placement="bottomLeft" trigger="hover">
                <template #content>
                  <ul class="command-list">
                    <li v-for="command in item.commandList" :key="command" @click="commandOne(command)">{{ command
                      }}</li>
                  </ul>
                </template>
                <div class="answer">
                  <div class="answer-container" v-html="marked(item.answer)"></div>
                  <div class="handle">
                    <div class="copy-icon" @click="copyToClipboard(item.answer)">
                      <img :src="copy" alt="Copy Icon" />
                      复制
                    </div>
                    <div class="reload-icon"
                      @click="searchComment(item.question, formatSingelUserQuestion(item.question), false)">
                      <img :src="reload" alt="Reload Icon" />
                      重新回答
                    </div>
                  </div>
                </div>
              </a-popover>
              <div v-else class="answer">
                <!-- <div class="answer-container"
                  v-html="index + 1 === qa.length && !item.isCacheMsg ? displayedAnswer : item.answer">
                </div> -->
                <div class="answer-container" v-html="marked(item.answer)"></div>
                <div class="handle">
                  <div class="copy-icon" @click="copyToClipboard(item.answer)">
                    <img :src="copy" alt="Copy Icon" />
                    复制
                  </div>
                  <div class="reload-icon"
                    @click="searchComment(item.question, formatSingelUserQuestion(item.question), false)">
                    <img :src="reload" alt="Reload Icon" />
                    重新回答
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="comment-wrap">
      <div class="container">
        <a-textarea trim @pressEnter="searchComment(comment, formatSingelUserQuestion(comment));"
          class="comment-textarea" :maxlength="4096" style="font-size: 16px" :autosize="{ minRows: 4, maxRows: 4 }"
          allowClear placeholder="请输入..." v-model:value="comment" />
        <a-tooltip title="查询">
          <div class="search" @click="searchComment(comment, formatSingelUserQuestion(comment))">
            <img :src="send" alt="Send" />
          </div>
        </a-tooltip>
      </div>
    </div>
    <!-- 返回首页 -->
    <div class="back-home" @click="router.push('/')">
      <a-tooltip title="回到首页">
        <img :src="backIcon" alt="back" style="width: 30px; height: 30px;" />
      </a-tooltip>
    </div>
  </div>
</template>

<style scoped lang="less">
.chat {
  font-family: Arial, sans-serif;
  line-height: 1.6;
}

.messages-wrap {
  position: absolute;
  width: 100%;
  left: 0;
  right: 0;
  top: 0;
  bottom: 150px;
  padding-top: 30px;
  min-height: 200px;
  overflow: auto;
  z-index: 1000;
}


.back-home {
  position: fixed;
  padding: 10px;
  margin-top: -25px;
  top: 50%;
  left: 10px;
  cursor: pointer;
  border-radius: 10px;
  background: #fff;
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.2);
  z-index: 1050;
}

.message {
  display: flex;
  align-items: flex-start;
}


.content {
  width: 100%;
}

.question-content {
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  margin-bottom: 10px;
}

.question {
  margin-right: 10px;
  margin-top: 6px;
  color: #fff;
  background-color: #3680FF;
  padding: 10px;
  border-radius: 10px;
  word-break: break-all;
  font-size: 14px;
  line-height: 20px;
  white-space: normal;
}

.question :deep(p),
.question :deep(ul),
.question :deep(ol),
.question :deep(li),
.question :deep(h1),
.question :deep(h2),
.question :deep(h3),
.question :deep(h4),
.question :deep(h5),
.question :deep(h6),
.question :deep(blockquote),
.question :deep(pre) {
  margin-bottom: 6px;
  line-height: 20px;
  padding-top: 0;
  padding-bottom: 0;
}

.avatar {
  min-width: 50px;
  width: 50px;
  height: 50px;
  margin-right: 10px;
}

.avatar img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
}


.answer-content {
  margin-bottom: 10px;
  display: flex;
  align-items: flex-start;
}

.answer {
  position: relative;
  max-width: calc(100% - 60px);
  margin-top: 6px;
  margin-left: 10px;
  text-align: left;
  color: #000;
  background-color: #fff;
  padding: 14px 10px 10px 10px;
  border-radius: 10px;
  word-break: break-all;
  font-size: 14px;
  line-height: 20px;
  cursor: pointer;
}

.answer-container {
  white-space: normal;
  line-height: 20px;
  width: 100%;
}

.answer-container :deep(p),
.answer-container :deep(ul),
.answer-container :deep(ol),
.answer-container :deep(li),
.answer-container :deep(h1),
.answer-container :deep(h2),
.answer-container :deep(h3),
.answer-container :deep(h4),
.answer-container :deep(h5),
.answer-container :deep(h6),
.answer-container :deep(blockquote),
.answer-container :deep(pre) {
  margin-bottom: 6px;
  line-height: 20px;
  padding-top: 0;
  padding-bottom: 0;
}

.handle {
  width: 200px;
  display: flex;
  font-size: 12px;
  opacity: 0;
  transition: opacity 0.3s;
}

.answer:hover {
  /* padding-bottom: 26px; */
}

.answer:hover .handle {
  opacity: 1;
}

.copy-icon,
.reload-icon {
  margin-right: 10px;
  cursor: pointer;
  color: #858DA5;

  img {
    width: 16px;
    height: 16px;
  }
}

.command-list {
  width: auto;
  list-style: none;
  padding: 0;
}

.command-list li {
  position: relative;
  padding: 0 40px 0 0;
  margin-top: 10px;
  list-style: none;
  line-height: 26px;
  background: #fff;
  border-radius: 8px;
  cursor: pointer;

  &:after {
    content: '';
    position: absolute;
    right: 10px;
    top: 12px;
    width: 10px;
    height: 10px;
    border: 2px solid rgba(0, 0, 0, 0.4);
    transform: rotate(45deg);
    border-color: rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.4) transparent transparent;
    border-radius: 2px;
  }
}

.answer:hover .command-list {
  display: block;
}

.command-list li:hover {
  color: #666;
}



.comment-wrap {
  position: absolute;
  bottom: 30px;
  left: 0;
  width: 100%;
  z-index: 10;
}

.comment-textarea :deep(textarea) {
  padding-left: 10px;
  padding-right: 50px;
  font-size: 16px;
}

.search {
  position: absolute;
  right: 10px;
  bottom: 20px;
  width: 30px;
  height: 30px;
  cursor: pointer;
  z-index: 10;

  img {
    width: 30px;
    height: 30px;
  }
}
</style>
